package com.elasticsearch.elasticsearchapi;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;

import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * es优化的查询
 * @author ycx
 * @date 2025-05-16 22:16
 */
@SpringBootTest
public class OptimizeElasticsearchDemo {
    private static final String INDEX_NAME = "books";
    private  RestHighLevelClient client;
    public OptimizeElasticsearchDemo() {
        // 初始化Elasticsearch客户端
        client = new RestHighLevelClient(
            RestClient.builder(new HttpHost("localhost", 9200, "http")));
    }

    public void close() {
        try {
            if (client != null) {
                client.close();
            }
        } catch (IOException e) {
            System.err.println("Error closing Elasticsearch client: " + e.getMessage());
        }
    }

    // 初始化索引和测试数据
    public void initEnvironment() throws IOException {
        if (!indexExists(INDEX_NAME)) {
            createIndex(INDEX_NAME);
            initData();
            System.out.println("环境初始化完成");
        } else {
            System.out.println("索引已存在，跳过初始化");
        }
    }

    // 检查索引是否存在
    private boolean indexExists(String indexName) throws IOException {
        GetIndexRequest getIndexRequest = new GetIndexRequest(INDEX_NAME);
        Boolean exist = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        return exist;
    }

    // 创建索引并设置映射
    private void createIndex(String indexName) throws IOException {
        CreateIndexRequest request = new CreateIndexRequest(indexName);

        // 设置索引分片和副本
        request.settings(Settings.builder()
            .put("index.number_of_shards", 3)
            .put("index.number_of_replicas", 1)
        );

        // 定义映射，设置分词器
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        {
            builder.startObject("properties");
            {
                builder.startObject("id")
                    .field("type", "keyword")
                    .endObject();
                builder.startObject("title")
                    .field("type", "text")
                    .field("analyzer", "ik_max_word")
                    .field("search_analyzer", "ik_smart")
                    .endObject();
                builder.startObject("author")
                    .field("type", "text")
                    .field("analyzer", "ik_max_word")
                    .field("search_analyzer", "ik_smart")
                    .endObject();
                builder.startObject("category")
                    .field("type", "keyword")
                    .endObject();
            }
            builder.endObject();
        }
        builder.endObject();

        request.mapping(String.valueOf(builder));

        // 执行创建索引请求
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        if (!response.isAcknowledged()) {
            throw new IOException("创建索引失败");
        }
    }

    // 批量初始化测试数据
    public void initData() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();

        // 添加测试文档
        addBookToBulk(bulkRequest, "1", "Java编程思想", "Bruce Eckel", "计算机编程");
        addBookToBulk(bulkRequest, "2", "Effective Java", "Joshua Bloch", "计算机编程");
        addBookToBulk(bulkRequest, "3", "深入理解Java虚拟机", "周志明", "计算机编程");
        addBookToBulk(bulkRequest, "4", "数据结构与算法分析", "Mark Allen Weiss", "计算机基础");
        addBookToBulk(bulkRequest, "5", "活着", "余华", "文学");
        addBookToBulk(bulkRequest, "6", "平凡的世界", "路遥", "文学");
        addBookToBulk(bulkRequest, "7", "Elasticsearch实战", "Radek Simko", "大数据");
        addBookToBulk(bulkRequest, "8", "Python数据分析实战", "Sebastian Raschka", "大数据");
        addBookToBulk(bulkRequest, "9", "Spring实战", "Craig Walls", "计算机编程");
        addBookToBulk(bulkRequest, "10", "高性能MySQL", "Baron Schwartz", "数据库");

        // 执行批量操作
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        if (bulkResponse.hasFailures()) {
            System.err.println("批量索引文档失败: " + bulkResponse.buildFailureMessage());
        }
    }

    private void addBookToBulk(BulkRequest bulkRequest, String id, String title, String author, String category) {
        Map<String, Object> jsonMap = new HashMap<>();
        jsonMap.put("id", id);
        jsonMap.put("title", title);
        jsonMap.put("author", author);
        jsonMap.put("category", category);

        IndexRequest indexRequest = new IndexRequest(INDEX_NAME)
            .id(id)
            .source(jsonMap);

        bulkRequest.add(indexRequest);
    }

    // 执行搜索
    public List<Map<String, Object>> search(String keyword, int pageNum, int pageSize, String sortField, SortOrder sortOrder) throws IOException {
        System.out.printf("搜索关键词: '%s', 页码: %d, 每页大小: %d, 排序字段: %s, 排序顺序: %s%n",
            keyword, pageNum, pageSize, sortField, sortOrder);

        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 构建多字段搜索查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // 主搜索条件 - 多字段匹配
        MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(keyword, "title", "author", "category")
            .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
            .fuzziness("AUTO")
            .prefixLength(2); // 前两个字符不进行模糊

        boolQuery.must(multiMatchQuery);

        // 设置分页
        int from = (pageNum - 1) * pageSize;
        sourceBuilder.from(from);
        sourceBuilder.size(pageSize);

        // 设置排序
        if (sortField != null && !sortField.isEmpty()) {
            sourceBuilder.sort(SortBuilders.fieldSort(sortField).order(sortOrder));
        } else {
            // 默认按相关度降序排列
            sourceBuilder.sort(SortBuilders.scoreSort().order(SortOrder.DESC));
        }

        // 设置高亮
        sourceBuilder.highlighter(new HighlightBuilder()
            .field("title")
            .field("author")
            .preTags("<span style=\"color:red\">")
            .postTags("</span>"));

        sourceBuilder.query(boolQuery);
        searchRequest.source(sourceBuilder);

        // 执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        // 处理搜索结果
        List<Map<String, Object>> results = new ArrayList<>();
        System.out.printf("找到 %d 个匹配结果%n", searchResponse.getHits().getTotalHits().value);

        for (SearchHit hit : searchResponse.getHits()) {
            Map<String, Object> source = hit.getSourceAsMap();
            source.put("score", hit.getScore());

            // 处理高亮
            if (hit.getHighlightFields().get("title") != null) {
                source.put("title", hit.getHighlightFields().get("title").fragments()[0].string());
            }
            if (hit.getHighlightFields().get("author") != null) {
                source.put("author", hit.getHighlightFields().get("author").fragments()[0].string());
            }

            results.add(source);
        }

        return results;
    }

    public static void main(String[] args) {
        OptimizeElasticsearchDemo demo = new OptimizeElasticsearchDemo();

        try {
            // 初始化环境
            demo.initEnvironment();

            // 等待索引刷新
            Thread.sleep(2000);

            // 测试搜索
            printSearchResults(demo.search("Java", 1, 5, null, SortOrder.DESC));
            printSearchResults(demo.search("java", 1, 5, null, SortOrder.DESC)); // 测试不区分大小写
            printSearchResults(demo.search("数据", 1, 5, null, SortOrder.DESC));

            // 测试模糊搜索
            printSearchResults(demo.search("Elastic", 1, 5, null, SortOrder.DESC));
            printSearchResults(demo.search("余", 1, 5, null, SortOrder.DESC));

            // 测试分页和排序
            printSearchResults(demo.search("编程", 1, 3, "title.keyword", SortOrder.ASC));
            printSearchResults(demo.search("编程", 2, 3, "title.keyword", SortOrder.ASC));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            demo.close();
        }
    }

    private static void printSearchResults(List<Map<String, Object>> results) {
        results.forEach(result -> {
            System.out.printf("ID: %s, 书名: %s, 作者: %s, 分类: %s, 得分: %.2f%n",
                result.get("id"),
                result.get("title"),
                result.get("author"),
                result.get("category"),
                result.get("score"));
        });
        System.out.println("-----------------------------------");
    }
}
